
#include <stdlib.h>
#include <string.h>
#include "flash.h"
#include "sysdef.h"
#include "device.h"

#if CONFIG_FLASH_ONCHIP
extern int flash_write_onchip(uint32_t address, uint8_t *data, uint32_t len);
extern int flash_read_onchip(uint32_t address, uint8_t *data, uint32_t len);
extern int flash_erase_onchip(uint32_t address, uint32_t len);
extern int flash_open_onchip(void);
#endif

#if CONFIG_FLASH_W25QXX
extern int flash_open_w25qxx(flash_type_e flash_type, flash_cfg_t *config);
extern void flash_close_w25qxx(flash_type_e flash_type);
extern int flash_write_w25qxx(flash_type_e flash_type, uint32_t address, uint8_t *data, uint32_t len);
extern int flash_read_w25qxx(flash_type_e flash_type, uint32_t address, uint8_t *data, uint32_t len);
extern int flash_erase_w25qxx(flash_type_e flash_type, uint32_t address, uint32_t len);
#endif

extern int device_find_by_class_port(dev_class_e class, int port);

static int flash_open(flash_type_e flash_type, flash_cfg_t *config) {
	int ret = -1;

    UNUSED(config);

    switch(flash_type) {
    #if CONFIG_FLASH_ONCHIP
        case FLASH_TYPE_ONCHIP: ret = flash_open_onchip(); break;
    #endif
    #if CONFIG_FLASH_W25QXX
        case FLASH_TYPE_W25QXX: ret = flash_open_w25qxx(flash_type, config); break;
    #endif
        default: ret = -1;
    }

    return ret;
}

static int flash_write(flash_type_e flash_type, uint32_t address, uint8_t *data, uint32_t len) {
	int ret = -1;
    OS_ERR err;
    flash_t *flash = flash_find_by_type(flash_type);
    OSMutexPend(&flash->mutex, OS_WAIT_FOREVER, OS_OPT_PEND_NON_BLOCKING, NULL, &err);
    switch(flash_type) {
    #if CONFIG_FLASH_ONCHIP
        case FLASH_TYPE_ONCHIP: ret = flash_write_onchip(address, data, len); break;
    #endif
    #if CONFIG_FLASH_W25QXX
        case FLASH_TYPE_W25QXX: ret = flash_write_w25qxx(flash_type, address, data, len); break;
    #endif
        default: ret = -1;
    }
    OSMutexPost(&flash->mutex, OS_OPT_POST_NONE, &err);
	return ret;
}

static int flash_read(flash_type_e flash_type, uint32_t address, uint8_t *data, uint32_t len) {
	int ret = -1;
    OS_ERR err;
    flash_t *flash = flash_find_by_type(flash_type);
    OSMutexPend(&flash->mutex, OS_WAIT_FOREVER, OS_OPT_PEND_NON_BLOCKING, NULL, &err);
    switch(flash_type) {
    #if CONFIG_FLASH_ONCHIP
        case FLASH_TYPE_ONCHIP: ret = flash_read_onchip(address, data, len); break;
    #endif
    #if CONFIG_FLASH_W25QXX
        case FLASH_TYPE_W25QXX: ret = flash_read_w25qxx(flash_type, address, data, len); break;
    #endif
        default: ret = -1;
    }
    OSMutexPost(&flash->mutex, OS_OPT_POST_NONE, &err);
	return ret;
}

static int flash_erase(flash_type_e flash_type, uint32_t address, uint32_t len) {
	int ret = -1;
    OS_ERR err;
    flash_t *flash = flash_find_by_type(flash_type);
    OSMutexPend(&flash->mutex, OS_WAIT_FOREVER, OS_OPT_PEND_NON_BLOCKING, NULL, &err);
    switch(flash_type) {
    #if CONFIG_FLASH_ONCHIP
        case FLASH_TYPE_ONCHIP: ret = flash_erase_onchip(address, len); break;
    #endif
    #if CONFIG_FLASH_W25QXX
        case FLASH_TYPE_W25QXX: ret = flash_erase_w25qxx(flash_type, address, len); break;
    #endif
        default: ret = -1;
    }
    OSMutexPost(&flash->mutex, OS_OPT_POST_NONE, &err);
	return ret;
}

static void flash_close(flash_type_e flash_type) {
    switch(flash_type) {
    #if CONFIG_FLASH_ONCHIP
        case FLASH_TYPE_ONCHIP: break;
    #endif
    #if CONFIG_FLASH_W25QXX
        case FLASH_TYPE_W25QXX: flash_close_w25qxx(flash_type); break;
    #endif
        default: break;
    }
}

static void flash_deinit(flash_type_e flash_type) {
    OS_ERR err;
    flash_t *flash = flash_find_by_type(flash_type);
    OSMutexDel(&flash->mutex, OS_OPT_DEL_ALWAYS, &err);
    free(flash);
}

#define FLASH_OPEN_FNC(e) int e##_OPEN (flash_cfg_t *cfg) { \
    return flash_open(e, cfg); \
}

#define FLASH_WRITE_FNC(e) int e##_WRITE (uint32_t address, uint8_t *data, uint32_t len) { \
    return flash_write(e, address, data, len); \
}

#define FLASH_READ_FNC(e) int e##_READ (uint32_t address, uint8_t *data, uint32_t len) { \
    return flash_read(e, address, data, len); \
}

#define FLASH_ERASE_FNC(e) int e##_ERASE (uint32_t address, uint32_t len) { \
    return flash_erase(e, address, len); \
}

#define FLASH_CLOSE_FNC(e) void e##_CLOSE (void) { \
    return flash_close(e); \
}

#define FLASH_DEINIT_FNC(e) void e##_DEINIT (void) { \
    return flash_deinit(e); \
}

FLASH_TYPE_LIST(FLASH_OPEN_FNC)
FLASH_TYPE_LIST(FLASH_WRITE_FNC)
FLASH_TYPE_LIST(FLASH_READ_FNC)
FLASH_TYPE_LIST(FLASH_ERASE_FNC)
FLASH_TYPE_LIST(FLASH_CLOSE_FNC)
FLASH_TYPE_LIST(FLASH_DEINIT_FNC)

#define FLASH_METHOD_PROC(e) case e: { \
    flash->open = e##_OPEN; \
    flash->write = e##_WRITE; \
    flash->read = e##_READ; \
    flash->erase = e##_ERASE; \
    flash->close = e##_CLOSE; \
    flash->deinit = e##_DEINIT; \
    break; \
}

flash_t *flash_init(flash_type_e flash_type) {
    if(flash_type >= FLASH_TYPE_MAX) return NULL;

    flash_t *flash = malloc(sizeof(flash_t));
    if(!flash) return NULL;

    memset(flash, 0, sizeof(flash_t));

    OS_ERR err;
	OSMutexCreate(&flash->mutex, "flash_mutex", &err);
	if(err != OS_ERR_NONE) {
        free(flash);
        return NULL;
    }

    switch(flash_type) {
        FLASH_TYPE_LIST(FLASH_METHOD_PROC)
        default: break;
    }

    return flash;
}

flash_t *flash_find_by_type(flash_type_e type) {
    return (flash_t *)device_find_by_class_port(DEV_CLASS_FLASH, (int)type);
}
